package 分布式锁.redis实现;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description: Redis分布式锁
 * @Author: 敖丙
 * @date: 2020-04-13
 **/
@Component
public class RedisLock {

    private String LOCK_KEY = "redis_lock";

    // 锁过期时间
    protected long INTERNAL_LOCK_LEASE_TIME = 200;

    // 尝试获取锁的时间
    private long timeout = 2000;

    private SetParams params = SetParams.setParams().nx().px(INTERNAL_LOCK_LEASE_TIME);

    JedisPool jedisPool = new JedisPool(new GenericObjectPoolConfig(), "101.132.236.231", 6379, 1000, "123456");

    /**
     * 加锁
     *
     * @param id
     * @return
     */
    public boolean lock(String id) {
        Long start = System.currentTimeMillis();
        Jedis jedis = jedisPool.getResource();
        try {
            for (; ; ) {
                //SET命令返回OK ，则证明获取锁成功
                String lock = jedis.set(LOCK_KEY, id, params);
                if ("OK".equals(lock)) {
                    return true;
                }
                //否则循环等待，在timeout时间内仍未获取到锁，则获取失败
                long l = System.currentTimeMillis() - start;
                if (l >= timeout) {
                    return false;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            jedis.close();
        }
    }

    /**
     * 解锁
     *
     * @param id
     * @return
     */
    public boolean unlock(String id) {
        Jedis jedis = jedisPool.getResource();
        String script =
                "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                        "   return redis.call('del',KEYS[1]) " +
                        "else" +
                        "   return 0 " +
                        "end";
        try {
            String result = jedis.eval(script, Collections.singletonList(LOCK_KEY), Collections.singletonList(id)).toString();
            return "1".equals(result) ? true : false;
        } finally {
            jedis.close();
        }
    }


    public static void main(String[] args) {
        RedisLock redisLock = new RedisLock();
//        String id = UUID.randomUUID().toString();
//        System.out.println(redisLock.lock(id));
//        System.out.println(redisLock.lock(id));

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 20; i++) {
            executorService.execute(() -> {
                String id = UUID.randomUUID().toString();
                Boolean lock = redisLock.lock(id);
                Boolean unlock = null;
                if (lock) {
                    // 获取锁后模拟业务操作的时间
                    try {
                        Thread.sleep(6000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    unlock = redisLock.unlock(id);
                }
                System.out.println(Thread.currentThread().getName() + " lock:" + lock + " unlock:" + unlock + " id:" + id);
            });
        }
        executorService.shutdown();


    }
}
